import { createParser } from "eventsource-parser";

import { fetch } from "./fetch";
import { streamAsyncIterable } from "./stream-async-iterable";

class ChatGPTError extends Error {
  statusCode?: number;
  statusText?: string;
  response?: Response;
  originalError?: Error;
}

export async function fetchSSE(
  url: string,
  options: Parameters<typeof fetch>[1] & {
    onMessage: (data: string) => void;
    onError: (response: Response) => void;
  }
) {
  const { onMessage, onError, ...fetchOptions } = options;
  const res = await fetch(url, fetchOptions);

  if (!res.ok) {
    onError(res);
    return;
  }

  const parser = createParser((event) => {
    if (event.type === "event") {
      onMessage(event.data);
    }
  });

  if (!res.body?.getReader) {
    // Vercel polyfills `fetch` with `node-fetch`, which doesn't conform to
    // web standards, so this is a workaround...
    const body: NodeJS.ReadableStream = res.body as any;

    if (!body.on || !body.read) {
      throw new ChatGPTError('unsupported "fetch" implementation');
    }

    body.on("readable", () => {
      let chunk: string | Buffer;
      while (null !== (chunk = body.read())) {
        parser.feed(chunk.toString());
      }
    });
  } else {
    for await (const chunk of streamAsyncIterable(res.body)) {
      const str = new TextDecoder().decode(chunk);
      parser.feed(str);
    }
  }
}
